/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Motion blur.
 * @remarks Adapted from the OSG example osgmotionblur.
 *//*
 * LEGAL:   COPYRIGHT (C) 2008 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#define FX_MOTION_BLUR_CC 1
#include "base/module.hh"
#include "base/stream.hh"
using namespace base;
#include "view/module.hh"
#include "view/window.hh"
using namespace view;
#include "fx/module.hh"
#include "fx/motion_blur.hh"

#include <osgViewer/GraphicsWindow>

namespace fx {

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////  MotionBlur  //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

MotionBlur* MotionBlur::msInstance;

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
MotionBlur::MotionBlur( void )
:   mEnabled(false),
    mOperation(new Operation)
{
    // NOP
}

MotionBlur::~MotionBlur()
{
    // NOP
}

/*****************************************************************************
 * Singleton method.
 *****************************************************************************/
MotionBlur&
MotionBlur:: GetInstance( void )
{
    if ( UX( msInstance == NULL ) )
        msInstance = new MotionBlur;
    return *msInstance;
}

/*****************************************************************************
 * Enable motion blur.
 *****************************************************************************/
void
MotionBlur::Enable( const fp persistence )
{
    if ( not mEnabled )
    {
        mEnabled = true;
        mOperation->SetPersistence( persistence );
        GET_WINDOW().OsgGetGraphicsWindow()->add( mOperation.get() );
        osg::DisplaySettings::instance()->setMinimumNumAccumBits( 8, 8, 8, 8 );
    }
}

/*****************************************************************************
 * Disable motion blur.
 *****************************************************************************/
void
MotionBlur::Disable( void )
{
    if ( mEnabled )
    {
        mEnabled = false;
        GET_WINDOW().OsgGetGraphicsWindow()->remove( mOperation.get() );
    }
}

////////////////////////////////////////////////////////////////////////////////
/////////////////////////  MotionBlur::Operation  //////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
MotionBlur::Operation::Operation( void )
:   osg::Operation("MotionBlur",true),
    mPersistence(0.0f),
    mCleared(false),
    mT0(0),
    mForceDisable(false)
{
    // NOP
}

MotionBlur::Operation::~Operation()
{
    // NOP
}

/*****************************************************************************
 * Change persistence of motion blur.
 *****************************************************************************/
void
MotionBlur::Operation::SetPersistence( const fp persistence )
{
    mPersistence = persistence;
}

/*****************************************************************************
 * Functor.
 *****************************************************************************/
void
MotionBlur::Operation::operator()( osg::Object* object )
{
    osg::GraphicsContext* gc = dynamic_cast<osg::GraphicsContext*>( object );
    if ( gc != NULL and not mForceDisable )
    {
        glGetError();  // clear error

        fp t = gc->getState()->getFrameStamp()->getSimulationTime();

        if ( not mCleared )
        {
            mCleared = true;
            mT0 = t;

            // Clear the accumulation buffer.
            glClearColor( 0, 0, 0, 0 );
            glClear( GL_ACCUM_BUFFER_BIT );
        }

        fp dt = ABS( t - mT0 );
        mT0 = t;

        // Compute the blur factor.
        fp s = std::pow( 0.2f, dt / mPersistence );

        // Scale, accumulate, and return.
        glAccum( GL_MULT, s );
        glAccum( GL_ACCUM, 1.0f - s );
        glAccum( GL_RETURN, 1.0f );

        // Older video cards lack an accumulation buffer.
        // Disabling stops OSG printing "render bin error".
        if ( UX( glGetError() != GL_NO_ERROR ) )
        {
            mForceDisable = true;
            CLOG << "OpenGL accumulation buffer error, disabling motion blur" << std::endl;
        }
    }
}

} // namespace fx
